home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Codigo / hh / rsource.exe / Heretic Source / NETOLD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-18  |  17.0 KB  |  784 lines

  1. // I_pcnet.m
  2.  
  3. #include "DoomDef.h"
  4. #include "P_local.h"
  5. #include "soundst.h"
  6.  
  7. #define NCMD_EXIT               0x80000000
  8. #define NCMD_RETRANSMIT 0x40000000
  9. #define NCMD_SETUP              0x20000000
  10. #define NCMD_KILL               0x10000000              // kill game
  11. #define NCMD_CHECKSUM   0x0fffffff
  12.  
  13.  
  14. doomcom_t               *doomcom;       
  15. doomdata_t              *netbuffer;             // points inside doomcom
  16.  
  17.  
  18. /*
  19. ==============================================================================
  20.  
  21.                             NETWORKING
  22.  
  23. gametic is the tic about to (or currently being) run
  24. maketic is the tick that hasn't had control made for it yet
  25. nettics[] has the maketics for all players 
  26.  
  27. a gametic cannot be run until nettics[] > gametic for all players
  28.  
  29. ==============================================================================
  30. */
  31.  
  32. #define RESENDCOUNT     10
  33. #define PL_DRONE        0x80                            // bit flag in doomdata->player
  34.  
  35. ticcmd_t                localcmds[BACKUPTICS];
  36.  
  37. ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
  38. int             nettics[MAXNETNODES];
  39. boolean                 nodeingame[MAXNETNODES];        // set false as nodes leave game
  40. boolean                 remoteresend[MAXNETNODES];      // set when local needs tics
  41. int                             resendto[MAXNETNODES];                  // set when remote needs tics
  42. int                             resendcount[MAXNETNODES];
  43.  
  44. int                             nodeforplayer[MAXPLAYERS];
  45.  
  46. int             maketic;
  47. int                             lastnettic, skiptics;
  48. int                             ticdup;         
  49. int                             maxsend;        // BACKUPTICS/(2*ticdup)-1
  50.  
  51. void D_ProcessEvents (void); 
  52. void G_BuildTiccmd (ticcmd_t *cmd); 
  53. void D_DoAdvanceDemo (void);
  54.  
  55. boolean                 reboundpacket;
  56. doomdata_t              reboundstore;
  57.  
  58.  
  59. int     NetbufferSize (void)
  60. {
  61.     return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]); 
  62. }
  63.  
  64. unsigned NetbufferChecksum (void)
  65. {
  66.     unsigned                c;
  67.     int             i,l;
  68.  
  69.     c = 0x1234567;
  70.  
  71. #ifdef NeXT
  72.     return 0;                       // byte order problems
  73. #endif
  74.  
  75.     l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
  76.     for (i=0 ; i<l ; i++)
  77.         c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
  78.  
  79.     return c & NCMD_CHECKSUM;
  80. }
  81.  
  82. int ExpandTics (int low)
  83. {
  84.     int     delta;
  85.     
  86.     delta = low - (maketic&0xff);
  87.     
  88.     if (delta >= -64 && delta <= 64)
  89.         return (maketic&~0xff) + low;
  90.     if (delta > 64)
  91.         return (maketic&~0xff) - 256 + low;
  92.     if (delta < -64)
  93.         return (maketic&~0xff) + 256 + low;
  94.         
  95.     I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
  96.     return 0;
  97. }
  98.  
  99.  
  100. //============================================================================
  101.  
  102.  
  103. /*
  104. ==============
  105. =
  106. = HSendPacket
  107. =
  108. ==============
  109. */
  110.  
  111. void HSendPacket (int node, int flags)
  112. {
  113.     netbuffer->checksum = NetbufferChecksum () | flags;
  114.  
  115.     if (!node)
  116.     {
  117.         reboundstore = *netbuffer;
  118.         reboundpacket = true;
  119.         return;
  120.     }
  121.  
  122.     if (demoplayback)
  123.         return;
  124.  
  125.     if (!netgame)
  126.         I_Error ("Tried to transmit to another node");
  127.         
  128.     doomcom->command = CMD_SEND;
  129.     doomcom->remotenode = node;
  130.     doomcom->datalength = NetbufferSize ();
  131.     
  132. if (debugfile)
  133. {
  134.     int             i;
  135.     int             realretrans;
  136.     if (netbuffer->checksum & NCMD_RETRANSMIT)
  137.         realretrans = ExpandTics (netbuffer->retransmitfrom);
  138.     else
  139.         realretrans = -1;
  140.     fprintf (debugfile,"send (%i + %i, R %i) [%i] "
  141.     ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
  142.     for (i=0 ; i<doomcom->datalength ; i++)
  143.         fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
  144.     fprintf (debugfile,"\n");
  145. }
  146.  
  147.     I_NetCmd ();
  148. }
  149.  
  150. /*
  151. ==============
  152. =
  153. = HGetPacket
  154. =
  155. = Returns false if no packet is waiting
  156. =
  157. ==============
  158. */
  159.  
  160. boolean HGetPacket (void)
  161. {       
  162.     if (reboundpacket)
  163.     {
  164.         *netbuffer = reboundstore;
  165.         doomcom->remotenode = 0;
  166.         reboundpacket = false;
  167.         return true;
  168.     }
  169.  
  170.     if (!netgame)
  171.         return false;
  172.     if (demoplayback)
  173.         return false;
  174.         
  175.     doomcom->command = CMD_GET;
  176.     I_NetCmd ();
  177.     if (doomcom->remotenode == -1)
  178.         return false;
  179.  
  180.     if (doomcom->datalength != NetbufferSize ())
  181.     {
  182.         if (debugfile)
  183.             fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
  184.         return false;
  185.     }
  186.     
  187.     if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
  188.     {
  189.         if (debugfile)
  190.             fprintf (debugfile,"bad packet checksum\n");
  191.         return false;
  192.     }
  193.  
  194. if (debugfile)
  195. {
  196.     int             realretrans;
  197.             int     i;
  198.             
  199.     if (netbuffer->checksum & NCMD_SETUP)
  200.         fprintf (debugfile,"setup packet\n");
  201.     else
  202.     {
  203.         if (netbuffer->checksum & NCMD_RETRANSMIT)
  204.             realretrans = ExpandTics (netbuffer->retransmitfrom);
  205.         else
  206.             realretrans = -1;
  207.         fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
  208.         ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
  209.         for (i=0 ; i<doomcom->datalength ; i++)
  210.             fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
  211.         fprintf (debugfile,"\n");
  212.     }
  213. }
  214.     return true;    
  215. }
  216.  
  217.  
  218. /*
  219. ===================
  220. =
  221. = GetPackets
  222. =
  223. ===================
  224. */
  225.  
  226. char    exitmsg[80];
  227.  
  228. void GetPackets (void)
  229. {
  230.     int             netconsole;
  231.     int             netnode;
  232.     ticcmd_t        *src, *dest;
  233.     int             realend;
  234.     int             realstart;
  235.                  
  236.     while (HGetPacket ())
  237.     {
  238.         if (netbuffer->checksum & NCMD_SETUP)
  239.             continue;               // extra setup packet
  240.             
  241.         netconsole = netbuffer->player & ~PL_DRONE;
  242.         netnode = doomcom->remotenode;
  243.         //
  244.         // to save bytes, only the low byte of tic numbers are sent
  245.         // Figure out what the rest of the bytes are
  246.         //
  247.         realstart = ExpandTics (netbuffer->starttic);           
  248.         realend = (realstart+netbuffer->numtics);
  249.         
  250.         //
  251.         // check for exiting the game
  252.         //
  253.         if (netbuffer->checksum & NCMD_EXIT)
  254.         {
  255.             if (!nodeingame[netnode])
  256.                 continue;
  257.             nodeingame[netnode] = false;
  258.             playeringame[netconsole] = false;
  259.             strcpy(exitmsg, "PLAYER 1 LEFT THE GAME");
  260.             S_StartSound(NULL, sfx_chat);
  261.             exitmsg[7] += netconsole;
  262.             //players[consoleplayer].message = exitmsg;
  263.             P_SetMessage(&players[consoleplayer], exitmsg, true);
  264. /*                      if (demorecording)
  265.                 G_CheckDemoStatus ();
  266. */ // DEBUG
  267.             continue;
  268.         }
  269.  
  270.         //
  271.         // check for a remote game kill
  272.         //
  273.         if (netbuffer->checksum & NCMD_KILL)
  274.             I_Error ("Killed by network driver");
  275.  
  276.         nodeforplayer[netconsole] = netnode;
  277.         
  278.         //
  279.         // check for retransmit request
  280.         //
  281.         if ( resendcount[netnode] <= 0 
  282.         && (netbuffer->checksum & NCMD_RETRANSMIT) )
  283.         {
  284.             resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
  285. if (debugfile)
  286. fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
  287.             resendcount[netnode] = RESENDCOUNT;
  288.         }
  289.         else
  290.             resendcount[netnode]--;
  291.  
  292.         //
  293.         // check for out of order / duplicated packet
  294.         //              
  295.         if (realend == nettics[netnode])
  296.             continue;
  297.             
  298.         if (realend < nettics[netnode])
  299.         {
  300. if (debugfile)
  301. fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
  302.             continue;
  303.         }
  304.  
  305.         //
  306.         // check for a missed packet
  307.         //
  308.         if (realstart > nettics[netnode])
  309.         {
  310.         // stop processing until the other system resends the missed tics
  311. if (debugfile)
  312. fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
  313.             remoteresend[netnode] = true;
  314.             continue;
  315.         }
  316.     
  317. //
  318. // update command store from the packet
  319. //
  320. {
  321.     int             start;
  322.  
  323.         remoteresend[netnode] = false;
  324.         
  325.         start = nettics[netnode] - realstart;           
  326.         src = &netbuffer->cmds[start];
  327.  
  328.         while (nettics[netnode] < realend)
  329.         {
  330.             dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
  331.             nettics[netnode]++;
  332.             *dest = *src;
  333.             src++;
  334.         }
  335.     }
  336. }
  337.  
  338. }
  339.  
  340. /*
  341. =============
  342. =
  343. = NetUpdate
  344. =
  345. = Builds ticcmds for console player
  346. = sends out a packet
  347. =============
  348. */
  349.  
  350. int      gametime;
  351.  
  352. void NetUpdate (void)
  353. {
  354.     int             nowtime;
  355.     int             newtics;
  356.     int                             i,j;
  357.     int                             realstart;
  358.     int                             gameticdiv;
  359.             
  360. //
  361. // check time
  362. //  
  363.     nowtime = I_GetTime ()/ticdup;
  364.     newtics = nowtime - gametime;
  365.     gametime = nowtime;
  366.     
  367.     if (newtics <= 0)                       // nothing new to update
  368.         goto listen; 
  369.  
  370.     if (skiptics <= newtics)
  371.     {
  372.         newtics -= skiptics;
  373.         skiptics = 0;
  374.     }
  375.     else
  376.     {
  377.         skiptics -= newtics;
  378.         newtics = 0;
  379.     }
  380.     
  381.         
  382.     netbuffer->player = consoleplayer;
  383.         
  384. //
  385. // build new ticcmds for console player
  386. //
  387.     gameticdiv = gametic/ticdup;
  388.     for (i=0 ; i<newtics ; i++)
  389.     {
  390.         I_StartTic ();
  391.         D_ProcessEvents ();
  392.         if (maketic - gameticdiv >= BACKUPTICS/2-1)
  393.             break;          // can't hold any more
  394. //printf ("mk:%i ",maketic);
  395.         G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
  396.         maketic++;
  397.     }
  398.  
  399.  
  400.     if (singletics)
  401.         return;         // singletic update is syncronous
  402.         
  403. //
  404. // send the packet to the other nodes
  405. //
  406.     for (i=0 ; i<doomcom->numnodes ; i++)
  407.         if (nodeingame[i])
  408.         {
  409.             netbuffer->starttic = realstart = resendto[i];
  410.             netbuffer->numtics = maketic - realstart;
  411.             if (netbuffer->numtics > BACKUPTICS)
  412.                 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
  413.  
  414.             resendto[i] = maketic - doomcom->extratics;
  415.     
  416.  
  417.             for (j=0 ; j< netbuffer->numtics ; j++)
  418.                 netbuffer->cmds[j] = 
  419.                     localcmds[(realstart+j)%BACKUPTICS];
  420.                     
  421.             if (remoteresend[i])
  422.             {
  423.                 netbuffer->retransmitfrom = nettics[i];
  424.                 HSendPacket (i, NCMD_RETRANSMIT);
  425.             }
  426.             else
  427.             {
  428.                 netbuffer->retransmitfrom = 0;
  429.                 HSendPacket (i, 0);
  430.             }
  431.         }
  432.  
  433. //
  434. // listen for other packets
  435. //              
  436. listen:
  437.  
  438.     GetPackets ();
  439. }
  440.  
  441.  
  442. /*
  443. =====================
  444. =
  445. = CheckAbort
  446. =
  447. =====================
  448. */
  449.  
  450. void CheckAbort (void)
  451. {
  452.     event_t *ev;
  453.     int             stoptic;
  454.     
  455.     stoptic = I_GetTime () + 2; 
  456.     while (I_GetTime() < stoptic) 
  457.         I_StartTic (); 
  458.     
  459.     I_StartTic ();
  460.     for ( ; eventtail != eventhead 
  461.     ; eventtail = (++eventtail)&(MAXEVENTS-1) ) 
  462.     { 
  463.         ev = &events[eventtail]; 
  464.         if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
  465.             I_Error ("Network game synchronization aborted.");
  466.     } 
  467. }
  468.  
  469. /*
  470. =====================
  471. =
  472. = D_ArbitrateNetStart
  473. =
  474. =====================
  475. */
  476.  
  477. void D_ArbitrateNetStart (void)
  478. {
  479.     int             i;
  480.     boolean gotinfo[MAXNETNODES];
  481.     
  482.     autostart = true;
  483.     memset (gotinfo,0,sizeof(gotinfo));
  484.     
  485.     if (doomcom->consoleplayer)
  486.     {       // listen for setup info from key player
  487.         while (1)
  488.         {
  489.             CheckAbort ();
  490.             if (!HGetPacket ())
  491.                 continue;
  492.             if (netbuffer->checksum & NCMD_SETUP)
  493.             {
  494.                 if (netbuffer->player != VERSION)
  495.                     I_Error ("Different DOOM versions cannot play a net game!");
  496.                 startskill = netbuffer->retransmitfrom & 15;
  497.                 deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
  498.                 nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
  499.                 respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
  500.                 startmap = netbuffer->starttic & 0x3f;
  501.                 startepisode = netbuffer->starttic >> 6;
  502.                 return;
  503.             }
  504.         }
  505.     }
  506.     else
  507.     {       // key player, send the setup info
  508.         do
  509.         {
  510.             CheckAbort ();
  511.             for (i=0 ; i<doomcom->numnodes ; i++)
  512.             {
  513.                 netbuffer->retransmitfrom = startskill;
  514.                 if (deathmatch)
  515.                     netbuffer->retransmitfrom |= (deathmatch<<6);
  516.                 if (nomonsters)
  517.                     netbuffer->retransmitfrom |= 0x20;
  518.                 if (respawnparm)
  519.                     netbuffer->retransmitfrom |= 0x10;
  520.                 netbuffer->starttic = startepisode * 64 + startmap;
  521.                 netbuffer->player = VERSION;
  522.                 netbuffer->numtics = 0;
  523.                 HSendPacket (i, NCMD_SETUP);
  524.             }
  525.  
  526. #if 1
  527.             for(i = 10 ; i  &&  HGetPacket(); --i)
  528.             {
  529.  if((netbuffer->player&0x7f) < MAXNETNODES)
  530.                 gotinfo[netbuffer->player&0x7f] = true;
  531.             }
  532. #else
  533.             while (HGetPacket ())
  534.             {
  535.                 gotinfo[netbuffer->player&0x7f] = true;
  536.             }
  537. #endif
  538.  
  539.             for (i=1 ; i<doomcom->numnodes ; i++)
  540.                 if (!gotinfo[i])
  541.                     break;
  542.         } while (i < doomcom->numnodes);
  543.     }
  544. }
  545.  
  546. /*
  547. ===================
  548. =
  549. = D_CheckNetGame
  550. =
  551. = Works out player numbers among the net participants
  552. ===================
  553. */
  554.  
  555. extern  int                     viewangleoffset;
  556.  
  557. void D_CheckNetGame (void)
  558. {
  559.     int             i;
  560.     
  561.     for (i=0 ; i<MAXNETNODES ; i++)
  562.     {
  563.         nodeingame[i] = false;
  564.     nettics[i] = 0;
  565.         remoteresend[i] = false;        // set when local needs tics
  566.         resendto[i] = 0;                        // which tic to start sending
  567.     }
  568.     
  569. // I_InitNetwork sets doomcom and netgame
  570.     I_InitNetwork ();
  571.     if (doomcom->id != DOOMCOM_ID)
  572.         I_Error ("Doomcom buffer invalid!");
  573.     netbuffer = &doomcom->data;
  574.     consoleplayer = displayplayer = doomcom->consoleplayer;
  575.     if (netgame)
  576.         D_ArbitrateNetStart ();
  577. //printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
  578.     
  579. // read values out of doomcom
  580.     ticdup = doomcom->ticdup;
  581.     maxsend = BACKUPTICS/2-1;
  582.     if (maxsend<1)
  583.         maxsend = 1;
  584.             
  585.     for (i=0 ; i<doomcom->numplayers ; i++)
  586.         playeringame[i] = true;
  587.     for (i=0 ; i<doomcom->numnodes ; i++)
  588.         nodeingame[i] = true;
  589.     
  590. //printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
  591.  
  592. }
  593.  
  594. /*
  595. ==================
  596. =
  597. = D_QuitNetGame
  598. =
  599. = Called before quitting to leave a net game without hanging the
  600. = other players
  601. =
  602. ==================
  603. */
  604.  
  605. void D_QuitNetGame (void)
  606. {
  607.     int             i, j;
  608.     
  609.     if (debugfile)
  610.         fclose (debugfile);
  611.         
  612.     if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
  613.         return;
  614.     
  615. // send a bunch of packets for security
  616.     netbuffer->player = consoleplayer;
  617.     netbuffer->numtics = 0;
  618.     for (i=0 ; i<4 ; i++)
  619.     {
  620.         for (j=1 ; j<doomcom->numnodes ; j++)
  621.             if (nodeingame[j])
  622.                 HSendPacket (j, NCMD_EXIT);
  623.         I_WaitVBL (1);
  624.     }
  625. }
  626.  
  627.  
  628.  
  629. /*
  630. ===============
  631. =
  632. = TryRunTics
  633. =
  634. ===============
  635. */
  636.  
  637. int     frametics[4], frameon;
  638. int     frameskip[4];
  639. int             oldnettics;
  640. extern  boolean advancedemo;
  641.  
  642. void TryRunTics (void)
  643. {
  644.     int             i;
  645.     int             lowtic;
  646.     int             entertic;
  647.     static int              oldentertics;
  648.     int                             realtics, availabletics;
  649.     int                             counts;
  650.     int                             numplaying;
  651.  
  652. //
  653. // get real tics
  654. //                      
  655.     entertic = I_GetTime ()/ticdup;
  656.     realtics = entertic - oldentertics;
  657.     oldentertics = entertic;
  658.  
  659. //
  660. // get available tics
  661. //
  662.     NetUpdate ();
  663.     
  664.     lowtic = MAXINT;
  665.     numplaying = 0;
  666.     for (i=0 ; i<doomcom->numnodes ; i++)
  667.         if (nodeingame[i])
  668.         {
  669.             numplaying++;
  670.             if (nettics[i] < lowtic)
  671.                 lowtic = nettics[i];
  672.         }
  673.     availabletics = lowtic - gametic/ticdup;
  674.     
  675.  
  676. //
  677. // decide how many tics to run
  678. //
  679.     if (realtics < availabletics-1)
  680.         counts = realtics+1;
  681.     else if (realtics < availabletics)
  682.         counts = realtics;
  683.     else
  684.         counts = availabletics;
  685.     if (counts < 1)
  686.         counts = 1;
  687.         
  688.     frameon++;
  689.  
  690. if (debugfile)
  691.     fprintf (debugfile,"=======real: %i  avail: %i  game: %i\n",realtics, availabletics,counts);
  692.  
  693.     if (!demoplayback)
  694.     {       
  695.     //=============================================================================
  696.     //
  697.     //      ideally nettics[0] should be 1 - 3 tics above lowtic
  698.     //      if we are consistantly slower, speed up time
  699.     //
  700.         for (i=0 ; i<MAXPLAYERS ; i++)
  701.             if (playeringame[i])
  702.                 break;
  703.         if (consoleplayer == i)
  704.         {       // the key player does not adapt
  705.         }
  706.         else
  707.         {
  708.             if (nettics[0] <= nettics[nodeforplayer[i]])
  709.             {
  710.                 gametime--;
  711.     //                      printf ("-");
  712.             }
  713.             frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
  714.             oldnettics = nettics[0];
  715.             if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
  716.             {
  717.                 skiptics = 1;
  718.     //                      printf ("+");
  719.             }
  720.         }
  721.     //============================================================================= 
  722.     }       // demoplayback                 
  723.  
  724.     //
  725.     // wait for new tics if needed
  726.     //
  727.         while (lowtic < gametic/ticdup + counts)        
  728.         {
  729.     
  730.             NetUpdate ();   
  731.             lowtic = MAXINT;
  732.             
  733.             for (i=0 ; i<doomcom->numnodes ; i++)
  734.                 if (nodeingame[i] && nettics[i] < lowtic)
  735.                     lowtic = nettics[i];
  736.     
  737.             if (lowtic < gametic/ticdup)
  738.                 I_Error ("TryRunTics: lowtic < gametic");
  739.                 
  740.             // don't stay in here forever -- give the menu a chance to work
  741.             if (I_GetTime ()/ticdup - entertic >= 20)
  742.             {
  743.                 MN_Ticker ();
  744.                 return;
  745.             } 
  746.         }
  747.  
  748. //
  749. // run the count * ticdup dics
  750. //
  751.     while (counts--)
  752.     {
  753.         for (i=0 ; i<ticdup ; i++)
  754.         {
  755.             if (gametic/ticdup > lowtic)
  756.                 I_Error ("gametic>lowtic");
  757.             if (advancedemo)
  758.                 D_DoAdvanceDemo ();
  759.             MN_Ticker ();
  760.             G_Ticker ();
  761.             gametic++;
  762.             //
  763.             // modify command for duplicated tics
  764.             //
  765.             if (i != ticdup-1)
  766.             {
  767.                 ticcmd_t        *cmd;
  768.                 int                     buf;
  769.                 int                     j;
  770.                 
  771.                 buf = (gametic/ticdup)%BACKUPTICS; 
  772.                 for (j=0 ; j<MAXPLAYERS ; j++)
  773.                 {
  774.                     cmd = &netcmds[j][buf];
  775.                     cmd->chatchar = 0;
  776.                     if (cmd->buttons & BT_SPECIAL)
  777.                         cmd->buttons = 0;
  778.                 }
  779.             }
  780.         }
  781.         NetUpdate ();                                   // check for new console commands
  782.     }
  783. }
  784.